#include "log.h"
#include "args.h"
#include <fcntl.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#define TIME_LEN 32
#define TIME_FORMAT "%b/%d %T)"
#define MSG_LEN 512
#define LOG_FILE_SIZE_LIMIT (1<<16)
#define LOG_FILE_NUM_LIMIT 5
#define DIR_PATH_LEN 256
#define LOG_PATH "/var/log"
#define STR_INCLUDE(papa, son)						\
	strlen(papa) > strlen(son) &&					\
				   strncmp(papa,son,strlen(son)) == 0

int __fd = 0;
int __file_size = 0;

void reset_log_fd() {if (__fd>0) close(__fd); __fd = 0;}
void set_log_fd(int nfd) {
	reset_log_fd();
	__fd = nfd;
	struct stat stat;
	if (fstat(__fd, &stat)) {CAMERA_EXIT("Failed to fetch log file state");}
	else __file_size = stat.st_size;
}

void __rotate_log() 
{
	int min = 0x7fff, max = -1, cnt = 0;
	DIR* dir = NULL;
	struct dirent *iter = NULL;
    
	if (LOG_FILE_NUM_LIMIT <= 1) {
		unlink(LOG_TO);
		return;
	}

	dir = opendir(LOG_PATH);
	if (!dir) return;
	while (iter = readdir(dir)) {
		struct stat state;
		lstat(iter->d_name, &state);
		if (!S_ISREG(state.st_mode)) continue;
		if (STR_INCLUDE(iter->d_name, LOG_NAME)) {
			int num = atoi(iter->d_name + strlen(LOG_NAME) + 1);
			if (num < min) min = num;
			if (num > max) max = num;
			++cnt;
		}
	}

	char buf[DIR_PATH_LEN];
	memset(buf, 0, DIR_PATH_LEN);
	strcat(buf, LOG_TO);
	if (cnt) {
		int off = strlen(buf);
		while (cnt >= LOG_FILE_NUM_LIMIT-1) {
			sprintf(buf + off, ".%d", min++);
			unlink(buf);
			--cnt;
			buf[off] = '\0';
		}
		buf[off] = '\0';
		sprintf(buf + off, ".%d", max+1);
	} else strcat(buf, ".1");

	rename(LOG_TO, buf); 
	closedir(dir);
}

void __print_to_file(const char* msg) 
{
	if (__file_size >= LOG_FILE_SIZE_LIMIT) {
		close(__fd);
		__rotate_log();
		__fd = open(log_to, O_CREAT|O_WRONLY|O_APPEND);
		if (__fd < 0) {CAMERA_EXIT("Failed to rotate log file");}
		__file_size = 0;
	}

	__file_size += write(__fd, msg, strlen(msg));	
}

void get_time(char* buf) 
{
	time_t t;
	struct tm* tmp;
	t = time(NULL);
	tmp = localtime(&t);
	if (!tmp) return;
	strftime(buf, TIME_LEN, TIME_FORMAT, tmp);
}

char* trim(char* msg)
{
	char* new_msg = msg;
	while (!isalnum(*new_msg)) ++new_msg;
	while (!isalnum(msg[strlen(new_msg)-1]))
		new_msg[strlen(new_msg)-1] = '\0';
	return new_msg;
}

void print_log(const char* format, ...)
{
	char msg[MSG_LEN];
	memset(msg, 0, MSG_LEN);

	get_time(msg);
	msg[strlen(msg)] = ' ';

	va_list vaList;
	va_start(vaList, format);
	vsprintf(msg + strlen(msg), format, vaList);
	va_end(vaList);

	__print_to_file(trim(msg));
	__print_to_file("\n");
}
